declare interface Runner {
	ensured: boolean;
	callbacks: ((result: any) => void)[];
}

const runnerMap: { [key: string]: ((result: any) => void)[] } = {};

function waitFor(key: string) {
	return new Promise(res => {
		runnerMap[key].push((result: any) => res(result));
	});
}

function ensure(key: string, result: any) {
  if (!runnerMap[key]) {
    return;
  }
  runnerMap[key].forEach(fn => fn(result));
  delete runnerMap[key];
}

/**
 * 互斥注解
 * 用于保证某个方法同一时间只有单次调用
 * @param key 方法唯一键值
 * @returns 同一时间多次调用目标函数时只会执行一次，防止不必要的并发
 */
 export function Mutex(key: string) {
  return (target: any, property: string) => {
    let oriFn = target[property];
    Object.defineProperty(target, property, {
      value: async function () {
        if (runnerMap[key]) {
          return await waitFor(key);
        } else {
          runnerMap[key] = [];
        }
        let res = await Reflect.apply(oriFn, this, arguments || []);
        ensure(key, res);
        return res;
      }
    });
    return target[property];
  }
}